package com.sy.olddriver.listener;

import com.sy.olddriver.annotation.Controller;
import com.sy.olddriver.annotation.RequestMapping;
import com.sy.olddriver.constant.AppConstant;
import com.sy.olddriver.dto.HandlerMethod;
import com.sy.olddriver.exception.MVCException;
import com.sy.olddriver.utils.StringUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.File;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;

@WebListener
public class ApplicationContextListener implements ServletContextListener {
    /**
     * web.xml中用于保存表示模型所在包的包名
     * 这个值存放在Servlet上下文对象中
     */
    private static final String MODEL_PACKAGE_NAME_KEY = "modelPackageName";

    /**
     * 系统启动时扫描模型包下所有带有@Controller注解的模型类并
     * 将模型类中所有访问路径和方法信息一起存储在上下文对象中
     *
     * @param e
     */
    @Override
    public void contextInitialized(ServletContextEvent e) {
        System.out.println("应用初始化...");
        //用于存放访问路径和模型方法的映射信息
        Map<String, HandlerMethod> urlMappings = new LinkedHashMap<>();
        ServletContext ctx = e.getServletContext();
        //获取web.xml中存放model的包名
        String modelPackageName = String.valueOf(ctx.getInitParameter(MODEL_PACKAGE_NAME_KEY));
        //获取运行时所有实际存在的模型类
        String modelClassesDir = Thread.currentThread().getContextClassLoader().getResource("").getPath() + modelPackageName.replace(".", File.separator);
        File[] modelFiles = new File(modelClassesDir).listFiles();
        if (modelFiles != null) {
            for (File modelFile : modelFiles) {
                //获取所有运行时实际存在的模型类，不含class部分的名字，且不含$符号这样的内部类
                String modelFileName = modelFile.getName().replace(".class", "");
                //获取每个不含$模型类的类信息并进行实例化
                //并保存其访问路径和方法信息
                if (modelFileName.indexOf("$") == -1) {
                    //获取每个模型类的全类名
                    String modelClassFullName = modelPackageName + "." + modelFileName;
                    try {
                        //获取每个模型类的类信息
                        Class<?> modelClass = Class.forName(modelClassFullName);
                        //判断模型类是否带有Controller注解
                        if (modelClass.isAnnotationPresent(Controller.class)) {
                            //如果带有Controller注解就先实例化这个模型类，并进一步解析其类的访问属性以及方法的访问属性
                            Object modelObj = modelClass.newInstance();
                            //获取类上的RequestMapping注解
                            RequestMapping modelClassRequestMappingAnnotation = modelClass.getDeclaredAnnotation(RequestMapping.class);
                            //获取类上RequestMapping中的类访问路径,如果没有，默认为/
                            String classUrl;
                            if (modelClassRequestMappingAnnotation != null) {
                                classUrl = modelClassRequestMappingAnnotation.value();
                            } else {
                                classUrl = "/";
                            }
                            //当前类中所有带@RequestMapping注解的方法
                            Method[] methods = modelClass.getDeclaredMethods();
                            for (Method method : methods) {
                                if (method.isAnnotationPresent(RequestMapping.class)) {
                                    //得到方法上的@RequestMapping注解
                                    RequestMapping methodRequestMappingAnnotation = method.getDeclaredAnnotation(RequestMapping.class);
                                    //得到方法上的访问路径
                                    String methodurl = methodRequestMappingAnnotation.value();
                                    //完整路径
                                    String fullUrl = StringUtils.replaceMultileftSlashBars("/" + classUrl + "/" + methodurl);
                                    //是否存在方法和路径的映射信息
                                    if (urlMappings.get(fullUrl) != null) {
                                        throw new MVCException(fullUrl + "对应的访问路径重复");
                                    } else {
                                        //如果不存在映射信息则添加
                                        HandlerMethod handlerMethod = new HandlerMethod();
                                        handlerMethod.setModelObj(modelObj);
                                        handlerMethod.setMethod(method);
                                        urlMappings.put(fullUrl, handlerMethod);
                                    }
                                }
                            }
                        }
                    } catch (Exception e1) {
                        e1.printStackTrace();
                        throw new MVCException(e1);
                    }
                }
            }
        }
        ctx.setAttribute(AppConstant.URL_MAPPINGS, urlMappings);

    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}
